package xyz.xwls.bootadmin.sys.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import xyz.xwls.bootadmin.common.BootAdminException;
import xyz.xwls.bootadmin.common.ResultCode;
import xyz.xwls.bootadmin.sys.domain.Auth;
import xyz.xwls.bootadmin.sys.domain.User;
import xyz.xwls.bootadmin.sys.repository.UserRepository;
import xyz.xwls.bootadmin.sys.service.UserService;
import xyz.xwls.bootadmin.sys.vo.PageResult;
import xyz.xwls.bootadmin.sys.vo.UserVo;
import xyz.xwls.bootadmin.util.JwtTokenUtil;
import xyz.xwls.bootadmin.util.UpdateUtil;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author WZW
 * @date 2020/4/1
 */
@Slf4j
@Service
public class UserServiceImpl implements UserDetailsService, UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findUserByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        return user;
    }

    @Override
    public User getByUsername(String username) {
        return userRepository.findUserByUsername(username);
    }

    @Override
    public User register(User user) {
        if (userRepository.findUserByUsername(user.getUsername()) != null) {
            throw new BootAdminException();
        }
        String password = user.getPassword();
        user.setPassword(passwordEncoder.encode(password));
        return userRepository.save(user);
    }

    @Override
    public String login(String username, String password) {
        String token = null;
        try {
            UserDetails userDetails = this.loadUserByUsername(username);
            if (!passwordEncoder.matches(password, userDetails.getPassword())) {
                throw new BadCredentialsException("密码不正确");
            }
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            token = jwtTokenUtil.generateToken(userDetails);
        } catch (AuthenticationException e) {
            log.warn("登录异常:{}", e.getMessage());
            throw new BootAdminException(ResultCode.LOGIN_FAILED);
        }
        return token;
    }

    @Override
    public Set<Auth> getPermissionList(Long id) {
        Optional<User> optional = userRepository.findById(id);
        if (!optional.isPresent()) {
            return Collections.emptySet();
        }
        User user = optional.get();
        Set<Auth> authSet = new HashSet<>();
        user.getRoleList().forEach(role -> {
            Set<Auth> auths = role.getAuthSet();
            auths.forEach(auth -> {
                authSet.addAll(auths);
            });
        });
        return authSet;
    }

    @Override
    public List<UserVo> list() {
        List<User> userList = userRepository.findAll();
        return convert(userList);
    }

    @Override
    public PageResult<UserVo> list(int page, int limit, User user) {
        //查询条件中 status 值为2表示查询全部
        int allStatus = 2;
        if (Integer.valueOf(allStatus).equals(user.getStatus())) {
            user.setStatus(null);
        }
        ExampleMatcher exampleMatcher = ExampleMatcher.matching()
                .withMatcher("username", ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("mobile", ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("email", ExampleMatcher.GenericPropertyMatchers.contains());
        Example<User> userExample = Example.of(user, exampleMatcher);
        Pageable pageable = PageRequest.of(page - 1, limit);
        Page<User> userPage = userRepository.findAll(userExample, pageable);
        return PageResult.<UserVo>builder().page(page).limit(limit).total(userPage.getTotalElements())
                .totalPages(userPage.getTotalPages())
                .list(convert(userPage.toList())).build();
    }

    @Override
    public User save(User user) {
        return register(user);
    }

    @Override
    public User update(User user) {
        User source = userRepository.getOne(user.getId());
        UpdateUtil.copyNullProperties(source, user);
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        return userRepository.save(user);
    }

    public static UserVo convert(User user) {
        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(user, userVo);
        return userVo;
    }

    public static List<UserVo> convert(List<User> userList) {
        return userList.stream().map(UserServiceImpl::convert).collect(Collectors.toList());
    }
}
